﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using Sharp.DataTransferObject;
using Sharp.Infrastructure.WebApiClient;
using Sharp.Portal.Owin;
using Sharp.ServiceContracts;

namespace Sharp.Portal.Mvc.OAuth.Sharp.Server
{
    public class SharpOAuthAuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        private readonly string _asClientID = "as:client_id";

        private readonly string _asUserID = "as:user_id";

        private readonly string _asUserName = "as:user_name";

        private readonly string _asGrantType = "as:grant_type";

        private readonly string _asResponseType = "as:response_type";

        private readonly string _asClientData = "as:client_data";

        private readonly string _scopeAll = "user_all";



        public SharpOAuthAuthorizationServerProvider()
        {

        }

        /// <inheritdoc />
        /// <summary>
        /// Password
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");

            if (allowedOrigin == null) allowedOrigin = "*";

            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });


            context.OwinContext.Set(_asGrantType, Setting.GrantTypes.Password);

            var userManager = context.OwinContext.GetUserManager<SharpUserManager>();
            var user = await userManager.FindAsync(context.UserName, context.Password);

            if (user != null)
            {
                var oAuthIdentity = ClaimsIdentityCreate.GenerateUserIdentity(user, OAuthDefaults.AuthenticationType);

                //设置角色
                oAuthIdentity.AddRole(Setting.RoleConfig.AppUserAllRole);
                AuthenticationProperties properties = CreateProperties(user.Id, user.UserName);
                AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
                context.Validated(ticket);
                //设置上下文
                context.OwinContext.Set(_asClientID, context.ClientId);
                context.OwinContext.Set(_asUserID, user.Id.ToString());
                context.OwinContext.Set(_asUserName, context.UserName);
            }
            else
            {
                context.SetError("用户或者密码错误。");
            }
            await base.GrantResourceOwnerCredentials(context);
        }

        /// <inheritdoc />
        /// <summary>
        /// ClientCredentials
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
        {
            var oAuthIdentity = ClaimsIdentityCreate.GenerateAppIdentity(context.ClientId, "", OAuthDefaults.AuthenticationType);
            //设置角色
            oAuthIdentity.AddClaim(new Claim(oAuthIdentity.RoleClaimType, Setting.RoleConfig.AppRole));
            var ticket = new AuthenticationTicket(oAuthIdentity,
                new AuthenticationProperties() { AllowRefresh = true });
            context.Validated(ticket);
            //设置上下文
            context.OwinContext.Set(_asGrantType, Setting.GrantTypes.ClientCredentials);
            context.OwinContext.Set(_asClientID, context.ClientId);

            return base.GrantClientCredentials(context);
        }

        /// <inheritdoc />
        /// <summary>
        /// RefreshToken
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
        {
            context.OwinContext.Set(_asGrantType, Setting.GrantTypes.RefreshToken);
            context.OwinContext.Set(_asClientID, context.ClientId);
            var newId = new ClaimsIdentity(context.Ticket.Identity);
            var role = newId.FindFirstValue(newId.RoleClaimType);
            if (role != null)
            {
                if (role.Split(',').All(c => !c.Equals(Setting.RoleConfig.AppRole, StringComparison.OrdinalIgnoreCase)))
                {
                    context.OwinContext.Set(_asUserID, newId.GetUserId());
                }
            }
            var newTicket = new AuthenticationTicket(newId, context.Ticket.Properties);
            context.Validated(newTicket);
            return base.GrantRefreshToken(context);
        }

        /// <inheritdoc />
        /// <summary>
        /// AuthorizationCode
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
        {
            context.OwinContext.Set(_asGrantType, Setting.GrantTypes.AuthorizationCode);
            return base.GrantAuthorizationCode(context);
        }

        public override Task TokenEndpoint(OAuthTokenEndpointContext context)
        {
            foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
            {
                context.AdditionalResponseParameters.Add(property.Key, property.Value);
            }

            return Task.FromResult<object>(null);
        }

        /// <inheritdoc />
        /// <summary>
        /// 生成 authorization_code（authorization code 授权方式）、生成 access_token （implicit 授权模式）
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
        {
            var owinContext = context.OwinContext;
            context.OwinContext.Set(_asResponseType, context.AuthorizeRequest.ResponseType);
            if (context.Request.User?.Identity != null && context.Request.User.Identity.IsAuthenticated)
            {
                var userManager = context.OwinContext.GetUserManager<SharpUserManager>();
                var user = userManager.FindByName(context.Request.User.Identity.Name);

                var clientId = context.AuthorizeRequest.ClientId;

                ClaimsIdentity oAuthIdentity = ClaimsIdentityCreate.GenerateUserIdentity(user, OAuthDefaults.AuthenticationType);
                if (context.AuthorizeRequest.Scope.Any(t => t.Equals(_scopeAll, StringComparison.OrdinalIgnoreCase)))
                    oAuthIdentity.AddRole(Setting.RoleConfig.AppUserAllRole);
                else
                    oAuthIdentity.AddRole(Setting.RoleConfig.AppUserBaseRole);

                context.OwinContext.Set(_asClientID, clientId);
                context.OwinContext.Set(_asUserID, user.Id);
                context.OwinContext.Set(_asUserName, user.UserName);

                AuthenticationProperties properties = CreateProperties(user.Id, user.UserName);
                properties.RedirectUri = context.AuthorizeRequest.RedirectUri;

                owinContext.Authentication.SignIn(oAuthIdentity);
                context.RequestCompleted();
            }
            await base.AuthorizeEndpoint(context);
        }

        public override Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context)
        {
            return base.AuthorizationEndpointResponse(context);
        }

        public override Task TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
        {
            return base.TokenEndpointResponse(context);
        }

        public override Task MatchEndpoint(OAuthMatchEndpointContext context)
        {
            return base.MatchEndpoint(context);
        }


        /// <inheritdoc />
        /// <summary>
        /// 验证Client的身份（ClientId以及ClientSecret）
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        /// <remarks>这是内部系统使用的。</remarks>
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            if (context.TryGetFormCredentials(out var clientId, out var clientSecret)
                || context.TryGetBasicCredentials(out clientId, out clientSecret))
            {

                if (string.IsNullOrEmpty(clientId))
                {
                    context.SetError("client_id不可为空");
                    await base.ValidateClientAuthentication(context);
                    return;
                }

                OAuthConfigDTO sys;
                using (var proxy = new WebApiClientServiceProxy<IPlatformService>())
                {
                    sys = proxy.Channel.GetOAuthConfigs().FirstOrDefault(x => x.Interface == OAuthConfigDTO.OAuthType.Platform);
                }

                if (sys == null)
                {
                    context.SetError("client_id不存在应用");
                    await base.ValidateClientAuthentication(context);
                    return;
                }
                if (sys.AppId == clientId && sys.AppSecret == clientSecret)
                {
                    context.Validated(clientId);
                    context.OwinContext.Set(_asClientData, sys);
                }
            }
            await base.ValidateClientAuthentication(context);
        }

        /// <inheritdoc />
        /// <summary>
        /// 验证Client的重定向Url
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        /// <remarks>访问GrantCode/Authorize 会在这里验证</remarks>
        public override async Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
        {
            OAuthConfigDTO sys;
            using (var proxy = new WebApiClientServiceProxy<IPlatformService>())
            {
                sys = proxy.Channel.GetOAuthConfigs().FirstOrDefault(x => x.Interface == OAuthConfigDTO.OAuthType.Platform);
            }
            if (sys == null)
            {
                throw new Exception("client_id不存在应用");
            }
            if (context.RedirectUri.Equals(sys.ReturnUrl, StringComparison.OrdinalIgnoreCase))
            {
                context.Validated(context.RedirectUri);
            }
            else
            {
                throw new Exception("回调地址验证不通过。");
            }
            context.OwinContext.Set(_asClientData, sys);
            await Task.FromResult<object>(null);
        }

        public override Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context)
        {
            return base.ValidateAuthorizeRequest(context);
        }

        public override Task ValidateTokenRequest(OAuthValidateTokenRequestContext context)
        {
            context.Validated();
            return base.ValidateTokenRequest(context);
        }

        public static AuthenticationProperties CreateProperties(int userId, string userName)
        {
            IDictionary<string, string> data = new Dictionary<string, string>
            {
                { "userId", userId.ToString() },
                { "userName", userName }
            };
            return new AuthenticationProperties(data);
        }

    }
}